/*
* ============================================================================
*  Name        : sea_atomic_op.cpp
*  Part of     : Finite state machine utilities
*  Description : Atomic operations
*  Version     : %version: %
*  
*
* ============================================================================
*/
#include "sea_atomic_op.h"
#include "sea_platform.h"

/**
 * @file sea_atomic_op.cpp
 * @brief
 *  Cross platform atomic operations 
 *
 * @ingroup Platforms
 */
using namespace sea;

// -------------------------------------------------------------------------------------------------

#if defined( SEA_OS_QT )

int OsAtomic::get( ){ return m_value.operator int( ); }
int OsAtomic::inc( int value /* = 1 */ ) { return m_value.fetchAndAddOrdered( value ) + value; }
int OsAtomic::dec( int value /* = 1 */ ) { return m_value.fetchAndAddOrdered( -value ) - value; }
int OsAtomic::set( int value ){ return m_value.fetchAndStoreOrdered( value ); }
int OsAtomic::compare_set( int expected_value, int value ) { return ( int )m_value.testAndSetOrdered( expected_value, value  ); }

#elif defined( SEA_OS_WIN32 )

int OsAtomic::get( ){ return ( int )m_value; }
#if 0
    int OsAtomic::inc( int value /* = 1 */ ) { return ( int )InterlockedAdd( &m_value, value ); }
    int OsAtomic::dec( int value /* = 1 */ ) { return ( int )InterlockedAdd( &m_value, -value ); }
#else
    int OsAtomic::inc( int value /* = 1 */ ) { SEA_UNUSED( value ); return ( int )InterlockedIncrement( &m_value ); }
    int OsAtomic::dec( int value /* = 1 */ ) { SEA_UNUSED( value ); return ( int )InterlockedDecrement( &m_value ); }
#endif
int OsAtomic::set( int value ){ return ( int )InterlockedExchange( &m_value, value ); }
int OsAtomic::compare_set( int expected_value, int value ) { return ( int )InterlockedCompareExchange( &m_value, value, expected_value ); }

// -------------------------------------------------------------------------------------------------

#elif defined( SEA_OS_LINUX )

int OsAtomic::get( ){ return ( int )m_value; }
#if defined( USE_GNU_ATOMIC )
    int OsAtomic::inc( int value /* = 1 */ ) { return __gnu_cxx::__exchange_and_add( &m_value, value ) + value; }
    int OsAtomic::dec( int value /* = 1 */ ) { return __gnu_cxx::__exchange_and_add( &m_value, -value ) - value; }
#else
    int OsAtomic::inc( int value /* = 1 */ ) { return __sync_fetch_and_add( &m_value, value ); }
    int OsAtomic::dec( int value /* = 1 */ ) { return __sync_fetch_and_sub( &m_value, value ); }
#endif
int OsAtomic::set( int value ){ return ( int )__sync_lock_test_and_set( &m_value, value ); }
int OsAtomic::compare_set( int expected_value, int value ) { return ( int )__sync_val_compare_and_swap( &m_value, expected_value, value ); }
   
// -------------------------------------------------------------------------------------------------

#elif defined( SEA_OS_SOS )

int OsAtomic::get( ){ return ( int )m_value; }
int OsAtomic::inc( int value /* = 1 */ ) { return __e32_atomic_add_rlx32( &m_value, value ) + value; }
int OsAtomic::dec( int value /* = 1 */ ) { return __e32_atomic_add_rlx32( &m_value, -value ) - value; }
int OsAtomic::set( int value ){ return ( int )__e32_atomic_swp_rlx32( &m_value, value ); }
int OsAtomic::compare_set( int expected_value, int value ) { return ( int )__e32_atomic_cas_rlx32( &m_value, &expected_value, value ); }

// -------------------------------------------------------------------------------------------------

#elif defined( SEA_OS_LESS )

template< typename T > 
T OsAtomic::as( ){ return reinterpret_cast< T >( m_value ); }
int OsAtomic::get( ){ return ( int )m_value; }
int OsAtomic::inc( int value /* = 1 */ ) { m_value += value; return m_value; }
int OsAtomic::dec( int value /* = 1 */ ) { m_value -= value; return m_value; }
int OsAtomic::set( int value ){ int old = m_value; m_value = value; return old; }
int OsAtomic::compare_set( int expected_value, int value )
{
    if ( m_value == expected_value )
    {
        m_value = value;
        return expected_value;
    }
    else
    {
        return expected_value;
    }
}

#else

#error Platform has not been defined

#endif

// -------------------------------------------------------------------------------------------------
